﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Http;
using System.Web.Http.Description;
using RestSharp;
using System.Web;
using Ymatou.ServiceCenter;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Remoting.Messaging;

namespace Ymatou.ServiceCenter
{
    /// <summary>
    /// RPC注册的回调处理
    /// </summary>
    /// <param name="isAsyncInitSuccess"></param>
    public delegate void AsyncInitListener(bool isAsyncInitSuccess);
    
    /// <summary>
    /// 服务注册管理器
    /// </summary>
    public static class ServiceRegisterManager
    {

        /// <summary>
        /// RPC注册异常回调处理
        /// </summary>
        public static AsyncInitListener AsyncRpcInitListener;        

        /// <summary>
        /// RPC初始化
        /// </summary>
        /// <param name="assemblies">从指定的程序集中扫描，不指定则扫描应用下全部的程序集</param>
        public static void InitRpc(params Assembly[] assemblies)
        {
            try
            {
                Logger.Debug("服务注册开始");
                string appId = Utils.GetConsumerAppId();
                if (assemblies == null || assemblies.Length <= 0)
                {
                    assemblies = GetAllAssemblies().Where(am => !am.GlobalAssemblyCache).ToArray();
                }
                IList<ServiceRegisterAttribute> registerAttributes = GetServiceRegisterAttributes(assemblies) ??
                                                                     new List<ServiceRegisterAttribute>();

#if Net45
            //对WebApi做兼容处理，对外提供的接口可以不用打ServiceRegisterAttribute特性，直接从ApiExplorer获取
            try
            {
                Collection<ApiDescription> apiDescriptions = GlobalConfiguration.Configuration.Services.GetApiExplorer().ApiDescriptions;
                if (apiDescriptions != null)
                {
                    foreach (ApiDescription apiDescription in apiDescriptions)
                    {
                        if (apiDescription == null)
                            continue;
                        ServiceRegisterAttribute attribute = new ServiceRegisterAttribute
                        {
                            ApiName = apiDescription.RelativePath.Split('?')[0],
                            HttpMethod =
                                (Method)Enum.Parse(typeof(Method), apiDescription.HttpMethod.ToString(), true)
                        };
                        if (registerAttributes.FirstOrDefault(m => m != null && m.ApiName == attribute.ApiName && m.HttpMethod == attribute.HttpMethod) == null)
                            registerAttributes.Add(attribute);

                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error("ServiceRegisterManager,GetApiExplorer", ex);
            }
#endif
                //将ServiceRegisterAttribute转换为ServiceMetadata发送到服务中心
                IList<ServiceMetadata> serviceMetadatas = new List<ServiceMetadata>();
                foreach (ServiceRegisterAttribute item in registerAttributes)
                {
                    if (item == null)
                        continue;
                    ServiceMetadata serviceMetadata = new ServiceMetadata
                    {
                        AppId = appId,
                        Host = string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["AppHost"]) ? string.Format("http://{0}", appId) : ConfigurationManager.AppSettings["AppHost"],
                        ApiName = item.ApiName,
                        HttpMethod = item.HttpMethod.ToString(),
                        ApiPath = string.IsNullOrEmpty(item.ApiPath) ? item.ApiName : item.ApiPath,
                        Description = item.Description,
                        Owner = item.Owner
                    };
                    serviceMetadatas.Add(serviceMetadata);
                }

                bool flag = ServiceCenterFacade.SaveServiceMetadatas(serviceMetadatas);
                if(!flag)
                    throw new Exception("批量保存元数据出现异常！");
            }
            catch (Exception ex)
            {
                Logger.Error(string.Format("ServiceRegisterManager,服务注册发生异常：{0}", ex.Message), ex);
                throw;
            }
            Logger.Debug("服务注册结束");
        }

        /// <summary>
        /// RPC异步回调初始化
        /// </summary>
        /// <param name="assemblies">从指定的程序集中扫描，不指定则扫描应用下全部的程序集</param>
        /// <returns></returns>
        private static bool InitRpcCallBack(params Assembly[] assemblies)
        {
            try
            {
                InitRpc(assemblies);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 初始化扫描程序集中的ServiceRegisterAttribute特性
        /// </summary>
        /// <param name="assemblies">从指定的程序集中扫描，不指定则扫描应用下全部的程序集</param>
        public static void InitRpcAsync(params Assembly[] assemblies)
        {
            Func<Assembly[], bool> initHandler = new Func<Assembly[], bool>(InitRpcCallBack);

            Logger.Debug("（异步）开始服务注册");
            //异步操作接口(注意BeginInvoke方法的不同！)
            IAsyncResult result = initHandler.BeginInvoke(assemblies, new AsyncCallback(CallBackHandle), "AsycState:OK");
        }

        private static void CallBackHandle(IAsyncResult result)
        {
            Logger.Debug("（异步）完成服务注册");

            Func<Assembly[], bool> handler = (Func<Assembly[], bool>)((AsyncResult)result).AsyncDelegate;
            bool taskResult = handler.EndInvoke(result);

            Logger.Debug("（异步）服务注册结果：" + taskResult);

            if (AsyncRpcInitListener == null)
            {
                Logger.Debug("没有（异步）服务注册的结果处理函数");
            }
            else
            {
                Logger.Debug("调用（异步）服务注册的结果处理函数");
                AsyncRpcInitListener(taskResult);
            }
        }

        /// <summary>
        /// 扫描程序集中的ServiceRegisterAttribute特性
        /// </summary>
        /// <param name="assemblies">从指定的程序集中扫描，不指定则扫描应用下全部的程序集</param>
        /// <returns></returns>
        private static IList<ServiceRegisterAttribute> GetServiceRegisterAttributes(Assembly[] assemblies)
        {
            IList<ServiceRegisterAttribute> registerAttributes = new List<ServiceRegisterAttribute>();
            if (assemblies == null || assemblies.Length <= 0)
                return registerAttributes;
          
            foreach (var assembly in assemblies)
            {
                if (assembly == null)
                    continue;
                try
                {
                    if (assembly.IsDefined(typeof(RpcRegisterAssemblyAttribute), false))
                    {
                        Logger.Debug("assembly is using ServiceRegisterAttribute " + assembly.FullName);
                    }
                    else
                    {
                        //Logger.Debug("assembly is not using ServiceRegisterAttribute " + assembly.FullName);
                        continue;
                    }
                    
                    Type[] types = assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        IList<ServiceRegisterAttribute> typeAttributes = Utils.GetAttributes<ServiceRegisterAttribute>(type, true);
                        registerAttributes.AddList(typeAttributes);

                        MethodInfo[] methodInfos = type.GetMethods();
                        foreach (MethodInfo methodInfo in methodInfos)
                        {
                            try
                            {
                                IList<ServiceRegisterAttribute> methodInfoAttributes = Utils.GetAttributes<ServiceRegisterAttribute>(methodInfo, true);

                                if (methodInfoAttributes.Any())
                                {
                                    foreach (ServiceRegisterAttribute methodInfoAttribute in methodInfoAttributes)
                                    {
                                        Logger.Debug(string.Format("ServiceRegisterManager.Init.MethodInfo; apiName:{0}, HttpMethod:{1}, type name: {2}, method name: {3}", methodInfoAttribute.ApiName, methodInfoAttribute.HttpMethod, type.FullName, methodInfo.Name));
                                        registerAttributes.Add(methodInfoAttribute);
                                    }
                                }
                            }
                            catch (Exception ex3)
                            {
                                Logger.Error("ServiceRegisterManager.Init.MethodInfo:" + type.FullName, ex3);
                            }

                        }
                    }
                }
                catch (Exception ex1)
                {
                    Logger.Error("ServiceRegisterManager.Init.Assembly:" + assembly.FullName, ex1);
                }

            }

            return registerAttributes;
        }

        /// <summary>
        /// 获取所有的程序集
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<Assembly> GetAllAssemblies()
        {
            IList<Assembly> assemblys = AppDomain.CurrentDomain.GetAssemblies();
            string path = System.AppDomain.CurrentDomain.BaseDirectory;
            DirectoryInfo dir = new DirectoryInfo(path);
            foreach (FileInfo fileInfo in dir.GetFiles())
            {
                if (fileInfo == null || (fileInfo.Extension != ".dll" && fileInfo.Extension != ".exe"))
                    continue;
                try
                {
                    Assembly assembly = Assembly.LoadFile(fileInfo.FullName);
                    if (assembly == null || assemblys.Contains(assembly))
                        continue;
                    assemblys.Add(assembly);
                }
                catch
                {
                    // ignored
                }
            }
            return assemblys;
        }
    }

}
